package com.hznc.wcs.service.bpm.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.vo.Pagination;
import com.diboot.iam.entity.BaseLoginUser;
import com.diboot.iam.util.IamSecurityUtils;
import com.hznc.wcs.constans.BpmnModelConstants;
import com.hznc.wcs.convert.bpm.BpmProcessDefinitionConvert;
import com.hznc.wcs.dto.bpm.BpmModelMetaInfoRespDTO;
import com.hznc.wcs.entity.FormDesign;
import com.hznc.wcs.entity.bpm.BpmCategory;
import com.hznc.wcs.entity.bpm.BpmProcessDefinitionInfo;
import com.hznc.wcs.exception.BusinessException;
import com.hznc.wcs.mapper.BpmProcessDefinitionInfoMapper;
import com.hznc.wcs.service.FormDesignService;
import com.hznc.wcs.service.bpm.BpmCategoryService;
import com.hznc.wcs.service.bpm.BpmProcessDefinitionService;
import com.hznc.wcs.vo.bpm.BpmProcessDefinitionReqVO;
import com.hznc.wcs.vo.bpm.BpmProcessDefinitionRespVO;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description : 流程定义业务实现
 * @Date : 2024/6/20
 * @Author : ChenJin
 * @Version : v1.0.0
 **/
@Slf4j
@Service
public class BpmProcessDefinitionServiceImpl extends BaseServiceImpl<BpmProcessDefinitionInfoMapper, BpmProcessDefinitionInfo> implements BpmProcessDefinitionService {

    @Resource
    private RepositoryService repositoryService;
    
    @Resource
    private FormDesignService formDesignService;
    
    @Resource
    private BpmCategoryService bpmCategoryService;

    @Override
    public List<BpmProcessDefinitionRespVO> getBpmProcessDefinitionList(BpmProcessDefinitionReqVO reqVO, Pagination pagination) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.processDefinitionTenantId(currentUser.getTenantId());
        if (StrUtil.isNotBlank(reqVO.getKey())) {
            processDefinitionQuery.processDefinitionKey(reqVO.getKey());
        }
        if (StrUtil.isNotBlank(reqVO.getName())){
            processDefinitionQuery.processDefinitionNameLike("%"+reqVO.getName()+"%");
        }
        if (ObjectUtils.isEmpty(reqVO.getSuspensionState())||SuspensionState.ACTIVE.getStateCode()==reqVO.getSuspensionState()){
            processDefinitionQuery.active();
        }else {
            processDefinitionQuery.suspended();
        }
        long count = processDefinitionQuery.count();
        if (count == 0) {
            return new ArrayList<>();
        }
        List<ProcessDefinition> processDefinitionList;
        if (ObjectUtils.isEmpty(pagination)){
            pagination.setTotalCount(count);
            processDefinitionList = processDefinitionQuery.listPage(pagination.getPageIndex()-1, pagination.getPageSize());
        }else {
            processDefinitionList = processDefinitionQuery.list();
        }
        if (CollectionUtils.isEmpty(processDefinitionList)){
            return new ArrayList<>();
        }
        //流程分类map
        List<String> categoryIds = processDefinitionList.stream().map(ProcessDefinition::getCategory).distinct().collect(Collectors.toList());
        List<BpmCategory> categoryList = bpmCategoryService.getEntityListByIds(categoryIds);
        Map<String, BpmCategory> idToCategoryMap = categoryList.stream().collect(Collectors.toMap(BpmCategory::getId, bpmCategory -> bpmCategory));
        //流程部署map
        List<String> deploymentIds = processDefinitionList.stream().map(ProcessDefinition::getDeploymentId).distinct().collect(Collectors.toList());
        List<Deployment> deploymentList = new ArrayList<>();
        for (String deploymentId:deploymentIds) {
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            deploymentList.add(deployment);
        }
        Map<String, Deployment> idToDeploymentMap = deploymentList.stream().collect(Collectors.toMap(Deployment::getId, deployment -> deployment));
        //流程定义Map
        List<String> definitionIds = processDefinitionList.stream().map(ProcessDefinition::getId).distinct().collect(Collectors.toList());
        LambdaQueryWrapper<BpmProcessDefinitionInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(BpmProcessDefinitionInfo::getProcessDefinitionId,definitionIds).eq(BpmProcessDefinitionInfo::isDeleted,0);
        List<BpmProcessDefinitionInfo> bpmProcessDefinitionInfoList = this.getEntityList(queryWrapper);
        Map<String, BpmProcessDefinitionInfo> definitionIdToBpmDefinitionMap = bpmProcessDefinitionInfoList.stream().collect(Collectors.toMap(BpmProcessDefinitionInfo::getProcessDefinitionId, bpmProcessDefinitionInfo -> bpmProcessDefinitionInfo));
        //formMap
        List<String> formIds = bpmProcessDefinitionInfoList.stream().map(BpmProcessDefinitionInfo::getFormId).distinct().collect(Collectors.toList());
        List<FormDesign> formList = formDesignService.getEntityListByIds(formIds);
        Map<String, FormDesign> idToFormMap = formList.stream().collect(Collectors.toMap(FormDesign::getId, form -> form));
        return BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(processDefinitionList,idToDeploymentMap,definitionIdToBpmDefinitionMap,idToFormMap,idToCategoryMap);
    }

    @Override
    public BpmProcessDefinitionRespVO getBpmProcessDefinition(String definitionId) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(definitionId);
        if (ObjectUtils.isEmpty(processDefinition)){
            return null;
        }
        //流程分类
        BpmCategory bpmCategory = bpmCategoryService.getEntity(processDefinition.getCategory());
        //流程部署
        Deployment deployment = null;
        String deploymentId = processDefinition.getDeploymentId();
        if (StringUtils.isNotEmpty(deploymentId)){
            deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        }
        //流程定义扩展信息
        BpmProcessDefinitionInfo bpmProcessDefinitionInfo = null;
        FormDesign formDesign = null;
        LambdaQueryWrapper<BpmProcessDefinitionInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(BpmProcessDefinitionInfo::getProcessDefinitionId,definitionId).eq(BpmProcessDefinitionInfo::isDeleted,0);
        List<BpmProcessDefinitionInfo> bpmProcessDefinitionInfoList = this.getEntityList(queryWrapper);
        if (!CollectionUtils.isEmpty(bpmProcessDefinitionInfoList)){
            bpmProcessDefinitionInfo = bpmProcessDefinitionInfoList.get(0);
            formDesign = formDesignService.getEntity(bpmProcessDefinitionInfo.getFormId());
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        return BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(processDefinition,deployment,bpmProcessDefinitionInfo,formDesign,bpmCategory,bpmnModel,null);

    }

    @Override
    public String getBpmProcessDefinitionIdByKey(String key) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().latestVersion().singleResult();
        if (ObjectUtils.isEmpty(processDefinition)){
            throw new BusinessException("流程模型标识不存在！");
        }
        return processDefinition.getId();
    }

    @Override
    @Transactional
    public String createProcessDefinition(Model model, BpmModelMetaInfoRespDTO modelMetaInfo, byte[] bpmnBytes, FormDesign form) {
        BaseLoginUser currentUser = IamSecurityUtils.getCurrentUser();
        //创建流程部署
        Deployment deployment = repositoryService.createDeployment()
                .key(model.getKey())
                .name(model.getName())
                .category(model.getCategory())
                .addBytes(model.getKey()+ BpmnModelConstants.BPMN_FILE_SUFFIX,bpmnBytes)
                .tenantId(currentUser.getTenantId())
                .disableSchemaValidation()
                .deploy();
        //设置流程定义的分类
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        repositoryService.setProcessDefinitionCategory(processDefinition.getId(),model.getCategory());
        // 注意 1，ProcessDefinition 的 key 和 name 是通过 BPMN 中的 <bpmn2:process /> 的 id 和 name 决定
        // 注意 2，需要保证 Model、Deployment、ProcessDefinition 使用相同的 key，保证关联性。
        if (!Objects.equals(processDefinition.getKey(), model.getKey())) {
            throw new RuntimeException("");
        }
        if (!Objects.equals(processDefinition.getName(), model.getName())) {
            throw new RuntimeException("");
        }
        //插入流程信息扩展表
        BpmProcessDefinitionInfo bpmProcessDefinitionInfo = BeanUtils.convert(modelMetaInfo, BpmProcessDefinitionInfo.class)
                .setModelId(model.getId()).setProcessDefinitionId(processDefinition.getId());
        if (form != null) {
            bpmProcessDefinitionInfo.setFormFields(form.getCustomerFormOptions()).setFormConf(form.getCustomerFormRule());
        }
        this.createEntity(bpmProcessDefinitionInfo);
        return processDefinition.getId();
    }

    @Override
    public void updateProcessDefinitionState(String definitionId, Integer state) {
        // 激活
        if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) {
            repositoryService.activateProcessDefinitionById(definitionId, false, null);
        }else if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) {
            // suspendProcessInstances = false，进行中的任务，不进行挂起。
            // 原因：只要新的流程不允许发起即可，老流程继续可以执行。
            repositoryService.suspendProcessDefinitionById(definitionId, false, null);
        }else {
            throw new BusinessException("状态值有误");
        }
    }
    
}
